package com.itheima.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.db.Page;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.itheima.common.CommonResult;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.pojo.UserInfo;
import com.itheima.service.UserService;
import com.itheima.thread.UserThreadLocal;
import com.itheima.utils.DateTimeUtils;
import com.itheima.vo.RecommendationVo;
import com.itheima.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.xml.transform.Result;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.itheima.constant.RedisConstant.CODE_KEY;
import static com.itheima.constant.RedisConstant.GEO_KEY;

/**
 * @author 王同学
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public UserVO login(User user) {
        //获取用户输入的验证码
        String code = user.getCode();
        String phone = user.getPhone();
        //获取redis里面的验证码
        String codes = redisTemplate.opsForValue().get(CODE_KEY + "_" + phone);
        //判断传进来的验证码和Redis存的是否一样
        if (!code.equals(codes)) {
            throw new RuntimeException("验证码比对校验失败~！");
        }

        //通过手机号查询数据库，并返回用户对象的结果
        User user1 = userMapper.seleteByPhone(phone);
        //然后判断是否为空\
        String id = IdUtil.getSnowflakeNextIdStr();
        if (user1 == null) {
            // TODO: 2022/12/15 有漏洞，如果没有信息应该跳转到新的页面让用户去填写新的信息
            //如果为空的话，那就是新用户
            //创建新用户,已经有的数据可以不添加，给新用户添加一些默认的信息
            user.setPhone(phone);
            user.setNickname("新用户" + System.currentTimeMillis());
            user.setId(id);
            user.setCity("中国大陆");
            //然后把这个对象写到数据库里面
            boolean res = userMapper.add(user);
            //写一个判断如果为true 那么就执行下面的代码接着往下走，如果为false就给他结束掉
            if (!res) {
                throw new RuntimeException("添加新用户失败~！");
            }
            //如果没什么问题，就往下走了
        }
        //token 唯一
        //用时间戳加上字符串返回上去  生成token
        // String token =System.currentTimeMillis()+ res.getId();
        //生成token方式二 用糊涂集成的jwt生成
        String key = "tanhua";
        //一个是string类型 另一个是obj类型\
        Map<String, Object> map = new HashMap<>();//里面不写默认是15也就是16的空间从0开始
        map.put("id", id);
        map.put("nickname", user.getNickname());
        String token = JWTUtil.createToken(map, key.getBytes());

        //存入redis数据库                          存入的时候转换成json格式，这样看着比较正常

        boolean firstLogin = false;
        //检查用户信息是否完善
        Integer sex = user.getSex();
        String city = user.getCity();
        String birthday = user.getBirthday();
        String userNickname = user.getNickname();
        //判断一下，这四个参数有一个为空都不行
        if (sex == null || StringUtils.isEmpty(city) || StringUtils.isEmpty(birthday)
                || StringUtils.isEmpty(userNickname)) {
            firstLogin = true;
        }
        UserVO userVO = new UserVO();
        userVO.setToken(token);
        userVO.setFirstLogin(firstLogin); //这个是用来判断是否为新用户的


        //返回用vo去返
        return userVO;
        //如果一致，并且有其他信息，那么生成token直接登录

        //如果有这个手机号码的用户，并且没有其他的信息，那么就给他跳转到填写注册信息，

        // 等他填写完成后 生成token 完成登录


    }

    @Override
    public boolean sendMsg(String phone) {
        //随机数生成6位数验证码
        int number = new Random().nextInt(100000) + 100000;
        log.info("验证码为：{}", number);
        //这边把kv值写进去   设置验证码过期时间x天
        redisTemplate.opsForValue().set(CODE_KEY + "_" + phone, number + "", 30L, TimeUnit.DAYS);
        //第二种过期时间设置方式，得用这个对象去点出来
        // redisTemplate.expire("", Duration.ofDays(1));

        return true;
    }

    @Override
    public boolean inof(UserInfo userInfo) {
        //1．判断入参是否为空
        if (userInfo == null) {
            throw new RuntimeException("入参不能为空~！");
        }
        // 2．通过ThreadLocaL获取当前线程用户的id（就是那个get/set方法）
        String userid = UserThreadLocal.get();
        //然后去查询这个用户id ，判断一下是否为空
        User user = userMapper.seleteById(userid);
        if (user == null) {
            throw new RuntimeException("用户不能为空");
        }
        //如果查到这个数据，那么就把他复制一份
        BeanUtils.copyProperties(userInfo, user);
        // 3．执行update操作
        boolean res = userMapper.updateById(user);
        if (!res) {
            throw new RuntimeException("更新用户信息失败");
        }

        //4．返回结果


        return res;
    }

    @Override
    public CommonResult getRecommendationList(double longitude, double latitude, Integer page, Integer pageSize) {

        // 1.入参校验
         /*   if (longitude == null || latitude == null){
                throw new RuntimeException("入参不能为空");
            }*/
        //2.以入参作为圆心，2公里作为半径，查询Redis
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                // 包含距离
                .includeDistance()
                // 包含经纬度
                .includeCoordinates()
                // 升序
                .sortAscending()
                // 最大查询数量
                .limit(1000);
        //设置圆心 传经纬度
        Point point=new Point(longitude,latitude);
        //设置半径
        Distance distance1=new Distance(2, Metrics.KILOMETERS);
        //画圆
        Circle circle=new Circle(point,distance1);
        //写入redis
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo().radius(GEO_KEY, circle, args);
        //这边可以做输出，主要用来查看结果，调试用的 转换诚json格式输出，然后放到api美化
        System.out.println(JSON.toJSONString(radius));

        List<String>idList=new ArrayList<>();
        // 3.从Redis得到一个id集合（周边用户的id>，一层一层往下走
        //先进一层
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = radius.getContent();
        //把这层下面的集合都遍历一下
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : content) {
            //再进去一层拿name
            RedisGeoCommands.GeoLocation<String> content1 = result.getContent();
            //拿到name
            String name = content1.getName();

            idList.add(name);
        }
        page=(page-1)*pageSize;
        // 4.以id集合作为条件，到mysqL查询用户信息
        //完善列表查询的分页功能 出参总数 total
        List<User> userList=userMapper.seleteByIds(idList,page,pageSize);
            //查总条数
        Long total=userMapper.selectCountById(idList);

        // 5.年龄、缘分值要额外处理
        //获取当前线程的用户信息
        User myInfo = userMapper.seleteById(UserThreadLocal.get());
        List<RecommendationVo> voList=new ArrayList<>();
            if (myInfo==null){
                throw new RuntimeException("查询不到当前的用户信息");
            }
            //获取我的年龄
        String myBirthday = myInfo.getBirthday();
        Integer myAge = DateTimeUtils.birthdayByAge(myBirthday);


        if (!CollectionUtil.isEmpty(userList)){
            for (User user : userList) {
                if (user==null){
                    continue;
                }
                String birthday= user.getBirthday();
                //得到相差的年龄
                Integer age = DateTimeUtils.birthdayByAge(birthday);
                //拿这个用户的年龄，和我的年龄对比，计算出缘分值
                Integer score = DateTimeUtils.getScore(myAge, age);

                RecommendationVo vo=new RecommendationVo();
                BeanUtils.copyProperties(user,vo);
                vo.setAge(age);
                vo.setScore(score);

                voList.add(vo);
            }
        }

        return CommonResult.list(voList,total);
    }
    @Override
    public boolean updateByPhone(String phone) {
        //1.入参校验
        if (phone==null||StringUtils.isEmpty(phone)){
            throw new RuntimeException("修改的手机号码不能为空~！");
        }
        //2.判断要修改的手机号码是否被占用
        User res = userMapper.seleteByPhone(phone);
        if (res!=null){
            throw new RuntimeException("修改的手机号码已被占用，请更换要修改的手机号码~！");
        }
        //3.修改手机号，并返回
        //获取当前线程的用户信息
        String userId = UserThreadLocal.get();
        //通过当前线程id去修改当前id的手机号

        return userMapper.updateByPhone(userId,phone);
    }

    @Override
    public boolean updateUserById(User user) {
        //1.入参校验
        if (user==null){
            throw new RuntimeException("修改的数据不能为空");
        }
        //2.获取当前线程的用户id
        String id = UserThreadLocal.get();
        //3.把数据写入sql
        //4.返回结果

        return userMapper.updateUserById(id,user);
    }

}
